# encoding: utf-8

import socket
import errno
import sys

tamanio_mensaje = 1024

class Conexion_cliente(object):
	def __init__(self,socket):
		self.socket = socket
		self.buffer = Buffer()
		
	"""	Ejecuta la primitiva recv sobre el socket self.socket.
	PRE : {self.socket es un descriptor de socket válido and self.socket está
	conectado}
	mensaje = recibir_mensaje()
	POST : {mensaje != None or (mensaje == None and el cliente se desconectó)}	"""
	def recibir_mensaje(self, esperar, tiempo_espera):
		# PRECONDICIÓN
		assert isinstance(self.socket, socket.socket)
        
		# (esperar == True )=> se necesita que definamos tiempo_espera como
        # límite de tiempo a la operación recv.  
		if esperar:
			self.socket.settimeout(tiempo_espera)
        
		try:
			mensaje = self.socket.recv(tamanio_mensaje)
			# Suponemos que nunca pueden ocurrir los errores ENOTSOCK (por 
			# precondición), EFAULT e EINVAL (por correcta implementación de
			# librerías de Python).
		except socket.error , descripcion :
			codigo_error = descripcion[0]
			sys.stderr.write("¡Problemas!: %s" % descripcion)
			if codigo_error == errno.EBADF:
				# Quizás el socket se ha cerrado y ya no es un descriptor de
				#conector válido.
				mensaje = None
			elif codigo_error == errno.ECONNREFUSED:
				# Seguir estudiando cuando puede ocurrir este error.
				mensaje = None
			elif codigo_error == errno.EINTR:
				# Seguir estudiando cuando puede ocurrir este error. El socket
				# estaba bloqueado en una recepción y lo hemos interrumpido
				# antes de que hubiera algún dato disponible.
				mensaje = None
			elif codigo_error == errno.ENOTCONN:
				# El usuario se desconectó.
				mensaje = None
		except socket.timeout:
			sys.stderr.write("¡Expiró el tiempo de espera!")
			mensaje = None
           
		return mensaje
	
	""" Envia 'mensaje' a traves del socket self.socket. Bloquea hasta que 
		 termina de enviar todo. 
	PRE : {self.socket es un descriptor de socket válido and self.socket está
	conectado and len(mensaje) <= tamanio_mensaje}
	codigo_error = enviar_mensaje(mensaje)
	POST : {(codigo_error == 0 and mensaje se envió correctamente) or 
	(codigo_error < 0 and no se pudo enviar mensaje)} """
	
	def enviar_mensaje(self,mensaje):
		# PRECONDICIÓN
		#assert len(mensaje) <= tamanio_mensaje
		codigo_error = 0
		tam = len(mensaje)
		tam_enviado = 0
		
		while tam_enviado < tam:
			# Cortar, de mensaje, lo que falta enviar
			porcion_restante = mensaje[int(tam_enviado):]
			try:
				tam_enviado = self.socket.send(porcion_restante)
			except socket.error , descripcion:
				codigo_error = descripcion[0]
				# Suponemos que nunca pueden ocurrir EBADF, ENOTSOCK, EWOULDBLOCK,
				# ENOTCONN
				sys.stderr.write("¡Problemas!: %s" % descripcion)
				if codigo_error == errno.EINTR:
					# Reintentamos
					self.enviar_mensaje(mensaje[porcion_restante:])
				elif codigo_error == errno.EPIPE:
					#WTF?
					codigo_error = -1
		
		return codigo_error
	
	"""	Ejecuta la primitiva close sobre el descriptor de socket self.socket.	
	PRE : {self.socket es un descriptor de socket válido and self.socket está 
	conectado}
	POST : {el descriptor self.socket se ha cerrado}
	"""
	def cerrar_conexion(self):
		try:
			self.socket.close()
		except socket.error , descripcion :
			codigo_error = descripcion[0]
			# Suponemos que nunca puede ocurrir EBADF
			sys.stderr.write("¡Problemas!: %s" % descripcion)
			if codigo_error == errno.EINTR:
				# Reintentamos
				self.cerrar_conexion()
			elif codigo_error == errno.EIO:
				#WTF?
				self.cerrar_conexion()
 
class Buffer(object):
	def __init__(self):
		self.mensaje = ""
	
	""" 
	PRE : {mensaje es un string and b == B and isinstance(B,Buffer)} 
	b.almacenar_mensaje(mensaje)
	POST : {b.devolver_mensaje() == B.devolver_mensaje()+mensaje} 
	"""
	def almacenar_mensaje(self,mensaje):
		self.mensaje += mensaje
		assert self.mensaje == self.mensaje[0:-len(mensaje)]+mensaje
	
	""" Devuelve una copia del mensaje almacenado en el buffer """	
	def devolver_mensaje(self):
		return str(self.mensaje)
		
	def isEmpty(self):
		return self.mensaje == ""
	
	def vaciar(self):
		self.mensaje = ""